/*
 * Copyright 2002-2023 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.framework;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.aopalliance.intercept.Interceptor;
import org.aopalliance.intercept.MethodInterceptor;

import org.springframework.aop.Advisor;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.adapter.AdvisorAdapterRegistry;
import org.springframework.aop.framework.adapter.GlobalAdvisorAdapterRegistry;
import org.springframework.lang.Nullable;

/**
 * A simple but definitive way of working out an advice chain for a Method,
 * given an {@link Advised} object. Always rebuilds each advice chain;
 * caching can be provided by subclasses.
 *
 * @author Juergen Hoeller
 * @author Rod Johnson
 * @author Adrian Colyer
 * @since 2.0.3
 */
@SuppressWarnings("serial")
public class DefaultAdvisorChainFactory implements AdvisorChainFactory, Serializable {

	/**
	 * Singleton instance of this class.
	 * @since 6.0.10
	 */
	public static final DefaultAdvisorChainFactory INSTANCE = new DefaultAdvisorChainFactory();

	/**
	 * 该函数getInterceptorsAndDynamicInterceptionAdvice的主要作用是根据给定的配置、方法和目标类，获取所有适用的拦截器和动态拦截建议。
	 * 1)函数首先通过GlobalAdvisorAdapterRegistry.getInstance()获取全局的AdvisorAdapterRegistry实例，
	 * 2)然后从配置中获取所有的Advisor。
	 * 3)接着，函数遍历这些Advisor，根据其类型（PointcutAdvisor或IntroductionAdvisor）和匹配条件，
	 * 4)将符合条件的拦截器添加到interceptorList中。
	 * 5)对于PointcutAdvisor，函数会进一步检查方法匹配器是否匹配目标方法，并根据匹配结果决定是否添加拦截器。
	 * 6)对于IntroductionAdvisor，函数会直接添加拦截器。最后，函数返回包含所有适用拦截器的列表。
	 *
	 * 在代码中，Advised config参数提供了AOP配置信息，Method method参数表示目标方法，Class<?> targetClass参数表示目标类。AdvisorAdapterRegistry registry用于获取拦截器，Advisor[] advisors存储了所有的Advisor，List<Object> interceptorList用于存储最终的拦截器列表。Class<?> actualClass表示实际的目标类，Boolean hasIntroductions用于判断是否存在引入（Introduction）。
	 *
	 * 该函数的核心逻辑是通过遍历Advisor并根据其类型和匹配条件，将符合条件的拦截器添加到列表中。这一过程确保了AOP框架能够在运行时动态地应用拦截器，从而实现方法级别的拦截和增强。在实际业务中，该函数通常被用于AOP代理的创建过程中，确保代理对象能够正确地应用拦截器和动态拦截建议，从而实现诸如事务管理、安全控制等功能。
	 * @param config the AOP configuration in the form of an Advised object
	 * @param method the proxied method
	 * @param targetClass the target class (may be {@code null} to indicate a proxy without
	 * target object, in which case the method's declaring class is the next best option)
	 * @return
	 */
	@Override
	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		//1、获取默认的 AdvisorAdapterRegistry实例，用于获取拦截器。
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		//2、获取代理对象中包含的所有Advisor。
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		//3、获取实际的目标类，如果没有提供目标类信息，则使用方法声明的类作为替代。
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor pointcutAdvisor) {
				// Add it conditionally.
				//4、判断当前advisor是否为PointcutAdvisor类型，如果是，则进一步判断其pointcut是否匹配目标类。
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					//5、如果目标类匹配，再判断方法是否匹配。
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher iamm) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = iamm.matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						//6、如果目标类和目标方法都匹配，则获取该advisor的拦截器。
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor ia) {
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

	/**
	 * Determine whether the Advisors contain matching introductions.
	 */
	private static boolean hasMatchingIntroductions(Advisor[] advisors, Class<?> actualClass) {
		for (Advisor advisor : advisors) {
			if (advisor instanceof IntroductionAdvisor ia && ia.getClassFilter().matches(actualClass)) {
				return true;
			}
		}
		return false;
	}

}
